extern "C"
{
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
}
#include <sstream>
#include "socket.h"

namespace night_sky
{
  ServerSocket::ServerSocket()
    : m_socket(-1)
  {}

  ServerSocket::~ServerSocket()
  {
    this->close();
  }

  bool ServerSocket::init()
  {
    int soc = ::socket(AF_INET, SOCK_STREAM, 0);
    if (soc < 0) {
      perror("socket");
      return false;
    }

    int opt = 1;
    if (::setsockopt(soc, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(int)) != 0) {
      perror("setsockopt");
      return false;
    }
    this->m_socket = soc;
    return true;
  }

  bool ServerSocket::bind(const int port) const
  {
    if (this->isClosed()) {
      errno = ENOTCONN;
      return false; // throw invalid_operation();
    }

    struct sockaddr_in my;
    ::memset(&my, 0, sizeof(my));

    my.sin_family      = AF_INET;
    my.sin_addr.s_addr = htonl(INADDR_ANY);
    my.sin_port        = htons(port);

    if (::bind(this->m_socket, (struct sockaddr*)&my, sizeof(my)) == -1) {
      perror("bind");
      return false;
    }
    return true;
  }

  bool ServerSocket::listen(const int backlog=-1) const
  {
    if (this->isClosed()) {
      errno = ENOTCONN;
      return false; // throw invalid_operation();
    }

    int bl = SOMAXCONN;
    if (0 <= backlog) {
      bl = backlog;
    }
    if (::listen(this->m_socket, bl) == -1) {
      perror("listen");
      return false;
    }
    return true;
  }

  // [multi-thread] メンバへは参照 only とする
  AcceptedSocket* ServerSocket::accept() const
  {
    if (this->isClosed()) {
      errno = ENOTCONN;
      return NULL; // throw invalid_operation();
    }

    struct sockaddr_in from;
    socklen_t len = sizeof(from);

    int accepted = ::accept(this->m_socket, (struct sockaddr*)&from, &len);
    if (accepted < 0) {
      // エラった
      if (errno != EINTR) {
        // 割り込みじゃないとき
        perror("accept");
      }
      // 割り込みの時は何もしない
      return NULL;
    }
    // 成功
    return new AcceptedSocket(accepted, from);
  }

  bool ServerSocket::isClosed() const
  {
    return this->m_socket < 0;
  }

  void ServerSocket::close()
  {
    if (this->isClosed()) {
      return;
    }
    ::close(this->m_socket);
    this->m_socket = -1;
  }

  // AcceptedSocket

  AcceptedSocket::AcceptedSocket(const int socket, const struct sockaddr_in& from)
    : m_socket(socket), m_from(from)
  {}

  AcceptedSocket::~AcceptedSocket()
  {
    this->close();
  }

  const struct sockaddr_in& AcceptedSocket::getSockAddrIn() const
  {
    return this->m_from;
  }

  std::string AcceptedSocket::recvUntil(const std::string& eom) const
  {
    char buf[512];
    std::ostringstream os;
    while(true) {
      int len = ::recv(this->m_socket, buf, sizeof(buf)-1, 0);
      if (len < 0) {
        perror("recv");
        break;
      }
      if (len == 0) {
        // コネクションが切断された
        break;
      }
      buf[len] = '\0';
      if (!eom.empty()) {
        char* p = strpbrk(buf, eom.c_str());
        if (p != NULL) {
          *p = '\0';
          os << buf;
          break;
        }
      }
      os << buf;
    }
    return os.str();
  }

  bool AcceptedSocket::send(const std::string& data) const
  {
    int len = ::send(this->m_socket, data.c_str(), data.length(), 0);
    if (len < 0) {
      perror("send");
      return false;
    }
    return true;
  }

  bool AcceptedSocket::isClosed() const
  {
    return this->m_socket < 0;
  }

  void AcceptedSocket::close()
  {
    if (this->isClosed()) {
      return;
    }
    ::close(this->m_socket);
    this->m_socket = -1;
  }
}

